/*
 * 1.interface是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合，有点类似c++中的
 纯虚函数，如果实现了interface中的所有方法，即该类/对象实现了该接口，
 也可以说是对象实例化了
 * 2.interface 类型
    格式：
    type interfaceName interface {
        // 方法列表
    }
    注：
    a. interface可以被任意对象实现， 一个类型/对象也可以实现多个interface
    b.方法不能重载，如eat() eatt(s string) 不能同时存在

 * 3.interface 值
    声明为interface类型的变量，可以存储任何实现了interface中所有方法的类型那个的变量(对象)
    注：类的值类型传递方法会自动生成对应的引用类型传递方法，反之不成立

 * 4.interface 组合
     将一个interface1嵌入到另一个interface2中，但是组合中不同有重复的方法
     注：
     a.只要2个接口中的方法列表相同(与顺序无关)，即为相同的接口，可以互相赋值
     b.interface1 的方法列表属于interface2的方法列表的子集，
     interface2可以赋值给interface1，反之不成立(因为方法的缺失)，
     interface2中的方法会覆盖interface1中的同名方法(类似c++中子类覆盖父类方法)，
     c.可以嵌入包中interface

 * 5.interface 查询
    通过查询可以判断接口指向的对象是否为某个类型
    通过查询可以将原来为子集的interface1转为interface2类型，
    即interface1就可以调用原来属于interface2独有的方法
    常见用法：
    if varName2, ok := varName1.(interface2|typeName); ok {
        //此时varName2的类型由interface1转为interface2
        //或者varName1不是typeName的类型
    }else {
        //不能转换interface，或者varName1不是typeName类型的变量
    }
    注：
    a.varName2保存varName1值，varName1为interface变量，()中为类型
    b.varName.(type)用于判断类型，此处的type关键字，不能用于switch外的逻辑中
 */

 // for example
 package main

 import "fmt"

 type Person struct {
     name string
     age int
 }

 func (p Person) printMsg() {
     fmt.Printf("i am %s, and my age is %d. \n", p.name, p.age)
 }

 func (p Person) eat(s string) {
     fmt.Printf("%s is eating %s ...\n", p.name, s)
 }

 func (p Person) drink(s string) {
     fmt.Printf("%s is drinking %s ...\n", p.name, s)
 }

 type People interface {
     printMsg()
     PeopleEat // 组合
     PeopleDrink
     //eat() // eat() 不能出现重复的方法
 }

/*
// 与上面等价
 type People interface {
     printMsg()
     eat()
     drink()
 }
 */

 type PeopleDrink interface {
     drink(s string)
 }

 type PeopleEat interface {
     eat(s string)
 }

 type PeopleEatDrink interface {
     eat(s string)
     drink(s string)
 }

 // 以上Person类型就实现了
 // People/PeopleDrink/PersonEat/PersonEatDrink interface类型

 type Foodie struct {
     name string
 }

 func (f Foodie) eat(s string) {
     fmt.Printf("I am foodie, %s. My favorite food is the %s.\n", f.name, s)
 }

 // Foodie类实现了PeopleEat interface类型

 func main() {
     // 定义一个People interface 类型的变量p1
     var p1 People
     p1 = Person{"Rain", 23}
     p1.printMsg()
     p1.drink("orange juice")

     // 同一类可以属于多个interface， 只要这个类实现了这个interface中的方法
     var p2 PeopleEat
     p2 = Person{"Sun", 24}
     p2.eat("chaffy dish")

     //不同类也可以实现同一个interface
     var p3 PeopleEat
     p3 = Foodie{"James"}
     p3.eat("noodle")

     // interface赋值
     p3 = p1 // p3中的方法会被p1中的覆盖
     p3.eat("noodle")

     //interface 查询
     // 将(子集)PersonEat转为People类型
     if p4, ok := p2.(People); ok {
         p4.drink("water")//调用People interface中有而PeopleEat中没有的方法
         fmt.Println(p4)
     }

     // 查询p2是否为Person类型变量
     if p5, ok := p2.(Person); ok {
         fmt.Println(p5, "type is Person")
         p5.drink("***")//此时也可以调用Person所有的方法
     }

     var p6 PeopleEat = Foodie{"Tom"}

     if p7, ok := p6.(People); ok {
         fmt.Println(p7)
     }else {
         fmt.Println("Error: can not convert")
     }

     if p8, ok := p6.(Foodie); ok {
         fmt.Println(p8, "type is Foodie")
     }
 }
